package sli.sli2.securedobject;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.util.ResourceBundle;

import org.bouncycastle.openssl.PEMReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sli.sli2.channels.Channel;
import sli.sli2.securedobject.service.AuthenticationService;


/**
 * Executable main class for the secured object, includes a command line for simulating terminal input
 * @author Group 8, Khassraf Roman, Mueller Robin, Zischka Stefan
 * 
 */
public class SecuredObject {
    private static int zoneId;
	private static String address;
	private static int port;
    private static Channel channel;
    private static final Logger log = LoggerFactory.getLogger(SecuredObject.class);
    
    private static PublicKey providerPublicKey;
    private static PrivateKey ownPrivateKey;
    
    private static String pathToPrivateKey;
    private static String pathToPublicKey;
    private static int seqNr;
    

	public static void main(String[] args) {
		
		readProperties();
		
		readKeys();
        
		Socket socket = connect();
		if (socket == null) {
            System.out.println("Connection to PCP failed.");
            return;
        }	
        try {
            authenticate(socket);
            parseCommandLineInput();
        } catch (Exception ex) {
            log.error("Console instantiation failed", ex);
        }
        seqNr = 1;
	}

	
	private static void readKeys() {
		
		Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

		PEMReader in = null;

		try {
			in = new PEMReader(new FileReader(pathToPrivateKey));
			KeyPair keyPair = (KeyPair) in.readObject();
			ownPrivateKey = keyPair.getPrivate();
			
			in = new PEMReader(new FileReader(pathToPublicKey));
			providerPublicKey = (PublicKey) in.readObject();
			
		} catch (FileNotFoundException e1) {
			System.out.println("Error: Keyfile not found");
			System.exit(1);
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
	}


	/**
	 * Starts parsing command line input simulating the terminal interaction
	 */
	private static void parseCommandLineInput() throws Exception {
		
		boolean running = true;
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String line;
        while (((line = reader.readLine()) != null) && running) {
            if (line.startsWith("!check") || line.startsWith("!logEvent")) {
                line += " seqNr:" + String.valueOf(seqNr);
                channel.send(line.getBytes());
                String[] tempRes = channel.receive().split("seqNr:");
                if (tempRes.length != 2) {
                    log.error("Did not receive a sequence number");
                }
                try {
                    int newSeq = Integer.parseInt(tempRes[1]);
                    if (newSeq == 0) {
                        log.error("Received invalid sequence number");
                    } else {
                        seqNr = ++newSeq;
                    }
                } catch (NumberFormatException ex) {
                    log.error("Did not receive a sequence number");
                }
                System.out.println("Response: " + tempRes[0]);
            } else {
                System.out.println("Unknown command.");
                System.out.println("!check auth:{authHash} zone:{zoneId} terminal:{terminalId} [pin:{pin}] [biometrichash:{biometricHash}]");
                System.out.println("!logEvent object:{objectId} terminal:{terminalId} notify:{sendNotification} [recipient:{recipient}] [channel:{channel}] message:{message}");
            }
        }		
	}

	private static Socket connect() {
		try {
            return new Socket(address, port);
		} catch (IOException | SecurityException | IllegalArgumentException e) {
			log.error("Connection failed", e);
            return null;
		}
	}

	private static void authenticate(Socket socket) throws Exception {
        
		AuthenticationService as = new AuthenticationService(
                zoneId,
                new BufferedReader(new InputStreamReader(socket.getInputStream())),
                new PrintWriter(socket.getOutputStream(), true), ownPrivateKey, providerPublicKey);
        channel = as.authenticate();
	}

	/**
	 * Reads all necessary property files, this includes but is not limited to:
	 * 	Public Key
	 * 	Private Key
	 * 	Properties (id, port, etc.)
	 */
	private static void readProperties() {
		ResourceBundle bundle = ResourceBundle.getBundle("securedObject");
		try {
            log.info("Parsing pcp connection properties from securedObject.properties.");
            zoneId = Integer.parseInt(bundle.getString("zoneId"));
            address = bundle.getString("pcp.address");
            port = Integer.parseInt(bundle.getString("pcp.port"));
            pathToPrivateKey = bundle.getString("privkey.path");
            pathToPublicKey = bundle.getString("pubkey.path");
        } catch (Exception ex) {
            log.error("Failed to parse securedObject.properties");
        }
	}

}
